<?php

/**
 * 异常处理
 *
 * 统一异常处理(系统抛出和用户抛出)，系统异常会由set_error_handler指定的函数抛出系统内置ErrorException。<br />
 * 异常统一由Exception Handler接收，统一处理。所有Exception Handler需要满足{@link IExceptionHandler}接口<br />
 * 系统内置抽象基础类{@link BaseExceptionHandler}及其具体实现{@link SparrowExceptionHandler}，做为Sparrow<br />
 * 默认异常处理器，该处理器可通过配置exception.handler指定更换。<br />
 * Sparrow异常处理机制 = PHP5异常处理机制 ＋ PHP4异常<br />
 * 标准的抛出异常方式
 * <code>
 * // 不带数据的
 * throw new Exception('无法连接到服务器')
 * // 带数据的
 * throw new Excepdata('无法连接到服务器', 0, array('error' => $errstr, 'errno' =>  $errno));
 * </code>
 *
 * @subpackage Exception
 */

// 将PHP的错误也使用抛出异常来统一处理
set_error_handler('system_error_handler');

/**
 * 系统异常接收器
 *
 * 接收由系统触发的错误，转换成Exception后再抛出(throw)，统一使用PHP5的异常处理机制。
 * <ol>
 * <li>注：使用该接收器统一系统异常处理机制并不会对系统或用户级别的错误报告设置(php.ini, error_reporting，错误抑制符),产生影响，所有错误报告设置都仍会生效，这是一个平滑的改变。</li>
 * <li>注：该函数是一个系统回调函数，用户无需手动调用。</li>
 * </ol>
 *
 * @author Garbin
 * @param  int  $errno  错误号
 * @param  string  $errstr  错误信息
 * @param  string  $errfile  错误所在的文件
 * @param  int  $errline  错误所在文件的行号
 * @return void
 **/
function system_error_handler($errno, $errstr, $errfile, $errline)
{
    if (($errno & error_reporting()) != $errno)
    {
        /* 错误被屏蔽时就不抛出异常，该处理就允许你在代码中照常使用error_reporting来控制错误报告 */
        return true;
    }

    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

/**
 * 异常处理器接口
 *
 * 定义异常处理器接口规范，所有需要接入Sparrow异常处理机制的异常处理器均需要满足此接口要求
 *
 * @author Garbin
 * @package Framework
 * @subpackage Exception
 **/
interface IExceptionHandler
{
    /**
     * 异常处理器入口
     *
     * 该方法接收异常，并将异常按名称转发至相应处理方法
     *
     * @author    Garbin
     * @access public
     * @param string $exception_name    异常类名
     * @param object $e 异常对象
     * @return void
     */
    public function handle($exception_name, $e);

    /**
     * 缺少异常处理方法
     *
     * {@link Exception}以及未指定异常处理方法的异常的处理方法。
     *
     * @author    Garbin
     * @access public
     * @param object $e 异常对象
     * @return void
     */
    public function handleException($e);
}
/////////////////////////////接口类 结束！！！////////////////////////////////////////


/**
 * 基础的异常处理器,抽象类
 *
 * 所有异常处理器都应从该类继承
 *
 * @abstract BaseExceptionHandler
 * @author Garbin
 **/
abstract class BaseExceptionHandler  implements IExceptionHandler
{
    /**
     * 异常处理入口
     *
     * 该方法会由系统调用，当异常发生时，系统会将异常的名称及异常对象传递给该方法处理，该方法接收后会根据异常<br />
     * 名称选择相应的异常处理方法，并将异常传递给其处理。比如此时系统抛出了异常
     * <code>throw new ErrorException(...);</code><br />
     * handle接收到的为ErrorException, $e，此时handle会寻找异常处理器中的{@link handleErrorException()}方法来处理，若无<br />
     * 相应方法，则会使用默认的异常处理方法{@link handleException())。
     *
     * @author Garbin
     * @access public
     * @param  string $exception_name 异常对象名
     * @param  object $e 异常对象
     * @return void
     **/
    public function handle($exception_name, $e)
    {
        $handle_func = 'handle' . $exception_name;
        if (!method_exists($this, $handle_func))
        {
            /* 若对应的异常处理方法不存在，则使用缺省的异常处理方法 */
            $this->handleException($e);
        }
        else
        {
            /* 否则使用相应的异常处理方法 */
            $this->$handle_func($e);
        }
    }

    /**
     * 处理Exception异常
     *
     * Exception为PHP内置的异常基础类，该方法用于处理该类异常，尚未指定处理方法的异常也由此方法处理。
     *
     * @author Garbin
     * @access protected
     * @param  object $e
     * @return void
     **/
    public function handleException($e)
    {
        echo $e;
    }
}

/**
 * Sparrow异常处理器
 *
 * Sparrow默认的异常处理器，共处理三类异常(Exception, Excepdata及系统抛出的异常ErrorException)
 *
 * @author Garbin
 **/
class SparrowExceptionHandler extends BaseExceptionHandler
{
    /**
     * 缺省的异常处理方法
     *
     * 处理Exception异常及尚未指定处理方法的异常
     *
     * @author Garbin
     * @access protected
     * @param  object $e 异常对象
     * @return void
     **/
    public function handleException($e)
    {
        $exception = '<div><pre>' . $e . '</pre></div>';

        echo $exception;
    }

    /**
     * 处理带数据的异常
     *
     * 处理带数据的异常{@link Excepdata}
     *
     * @author Garbin
     * @access public
     * @param  object $e 异常对象
     * @return void
     */
    public function handleExcepdata($e)
    {
        $exception = '<div><pre>' . $e . '</pre></div>';
        $extra = $e->getExtra();
        if ($extra !== null)
        {
            $extra = print_r($extra, true);
            $exception .= "<div><pre>The Exception extra is:<br />{$extra}</pre></div>";
        }

        echo $exception;
    }

    /**
     * 处理系统抛出的异常
     *
     * 处理系统抛出的异常ErrorException
     *
     * @author Garbin
     * @access public
     * @param  object $e    异常对象
     * @return void
     */
    public function handleErrorException($e)
    {
        $exception = 'error:<br /><div><pre>' . $e . '</pre></div>';

        echo $exception;
    }
}

/**
 * 带有扩展数据的异常类
 *
 * 有些时候，处理异常时我们需要获取该异常发生时的一些数据，以方便更好的处理，使用该类可以绑定数据至异常类<br />
 * 处理器接收到异常后，就可直接从异常获取相应数据。
 *
 * @author Garbin
 */
class Excepdata extends Exception
{
    /**
     * 扩展数据
     */
    protected $_extra = null;

    /**
     * 构造函数
     *
     * @author Garbin
     * @param  string $message  异常消息文本
     * @param  int    $code     异常代码
     * @param  array  $extra    扩展信息，当您需要为异常添加除文本信息外的其它数据时，使用该参数进行传递
     * @return void
     **/
    public function __construct($message, $code = 0, $extra = null)
    {
        parent::__construct($message, $code);
        $this->_extra = $extra;
    }

    /**
     * 获取额外传递的数据
     *
     * 通过该方法可以取出绑定在异常对象中的数据
     *
     * @author Garbin
     * @access public
     * @return array
     **/
    public function getExtra()
    {
        return $this->_extra;
    }

    /**
     * 返回异常字符串
     *
     * 以字符串形式返回异常
     *
     * @author Garbin
     * @access public
     * @return string
     **/
    public function __toString()
    {
        return parent::__toString();
    }
}
